#!/usr/bin/make -f

#
# \brief  Tool for building a binary archive from source
# \author Norman Feske
# \date   2016-05-17
#

define HELP_MESSAGE

  Build a binary archive from source

  usage:

    $(firstword $(MAKEFILE_LIST)) <src-name> SPEC=<spec> USER=<user> CCACHE=<ccache> DBG=<dbg>

    <src-name>   name of the source archive to build
    <spec>       build spec, e.g., x86_32, x86_64
    <user>       identity of the archive creator
    <ccache>     compiler calls will use the C/C++ compiler cache if this is 1
    <dbg>        build 'dbg' archives containing debug info files in addition
                 to the corresponding 'bin' archives if this is 1

endef

export GENODE_DIR := $(realpath $(dir $(MAKEFILE_LIST))/../../..)

include $(GENODE_DIR)/tool/depot/mk/front_end.inc


#
# Provide Genode build system with clean environment
#
unexport BASE_DIR
unexport SPECS
unexport SPEC_FILES
unexport LIB_CACHE_DIR
unexport LIB_DEP_FILE
unexport LIB_PROGRESS_LOG
unexport CONTRIB_DIR
unexport REPOSITORIES


#
# The target is the name of the archive
#
ARCHIVE := $(TARGET)

#
# Define location of source archive
#
RECIPE_DIR    := $(call recipe_dir,src/$(ARCHIVE))
REP_DIR       := $(RECIPE_DIR:/recipes/src/$(ARCHIVE)=)
DEPOT_API_DIR := $(DEPOT_DIR)/$(USER)/api
DEPOT_SRC_DIR := $(DEPOT_DIR)/$(USER)/src
DEPOT_BIN_DIR := $(DEPOT_DIR)/$(USER)/bin
DEPOT_DBG_DIR := $(DEPOT_DIR)/$(USER)/dbg


#
# Look up hash of the source archive from the src recipe
#

EXPECTED_SRC_HASH_FILE := $(RECIPE_DIR)/hash
SRC_HASH_FILE := $(wildcard $(EXPECTED_SRC_HASH_FILE))

checked_src_hash_file:
ifeq ($(SRC_HASH_FILE),)
	@$(ECHO) "Error: source-archive hash file missing,"
	@$(ECHO) "       expected at '$(EXPECTED_SRC_HASH_FILE)'"
	@false
else

SRC_HASH_FILE_CONTENT := $(shell cat $(SRC_HASH_FILE))
SRC_VERSION           := $(firstword $(SRC_HASH_FILE_CONTENT))
endif


#
# Look for source archive
#
# First try to locate the source archive in the depot, which succeeds only
# if the archive is given with a versioned name. The archive version is
# unspecified, we look up the version information from the archive recipe.
#

VERSIONED_ARCHIVE := $(ARCHIVE)

SRC_DIR := $(DEPOT_SRC_DIR)/$(VERSIONED_ARCHIVE)
SRC_DIR := $(if $(wildcard $(SRC_DIR)),$(SRC_DIR),)

checked_src_archive:
	@true

ifeq ($(SRC_DIR),)
VERSIONED_ARCHIVE := $(ARCHIVE)/$(SRC_VERSION)
SRC_DIR := $(DEPOT_SRC_DIR)/$(VERSIONED_ARCHIVE)
checked_src_archive: checked_src_hash_file
endif

ifeq ($(wildcard $(SRC_DIR)),)
checked_src_archive: error_missing_source_archive
endif

error_missing_source_archive:
	@$(ECHO) "Error: missing source archive $(SRC_DIR)"
	@false


#
# Check for missing SPEC argument
#

checked_spec_argument:
ifeq ($(SPEC),)
	@$(ECHO) "Error: missing SPEC argument"
	@false
endif


#
# Define build-directory location
#

DEPOT_BIN_ARCHIVE_DIR   := $(DEPOT_BIN_DIR)/$(SPEC)/$(VERSIONED_ARCHIVE)
DEPOT_DBG_ARCHIVE_DIR   := $(DEPOT_DBG_DIR)/$(SPEC)/$(VERSIONED_ARCHIVE)
DEPOT_ARCHIVE_BUILD_DIR := $(addsuffix .build,$(DEPOT_BIN_ARCHIVE_DIR))
DEPOT_BIN_HASH_FILE     := $(addsuffix .hash,$(DEPOT_BIN_ARCHIVE_DIR))


#
# Create archive build directory, which corresponds to a Genode build directory
#
# etc/build.conf: REPOSITORIES point to the source archive and all used api
# archive. The list of used api archive comes from the '<srcdir>/used_apis'
# file.
#

# if building a library, always incorporate the API implemented by the library
API_FILE := $(wildcard $(addsuffix /api,$(SRC_DIR)))
ifneq ($(API_FILE),)
USED_APIS += $(call file_content,$(API_FILE))
endif

# incorporate all APIs used by the source archive
USED_APIS_FILE := $(SRC_DIR)/used_apis
ifneq ($(wildcard $(USED_APIS_FILE)),)
USED_APIS += $(shell cat $(USED_APIS_FILE))
endif

BUILD_CONF := $(DEPOT_ARCHIVE_BUILD_DIR)/etc/build.conf
SPECS_CONF := $(DEPOT_ARCHIVE_BUILD_DIR)/etc/specs.conf
TOOLS_CONF := $(DEPOT_ARCHIVE_BUILD_DIR)/etc/tools.conf
BUILD_MK   := $(DEPOT_ARCHIVE_BUILD_DIR)/Makefile

# validate that all API archives exist
USED_API_DIRS    := $(addprefix $(DEPOT_API_DIR)/, $(USED_APIS))
MISSING_API_DIRS := $(filter-out $(wildcard $(USED_API_DIRS)), $(USED_API_DIRS))

checked_api_archives:
ifneq ($(MISSING_API_DIRS),)
	@($(ECHO) "Error: The following API archives are missing:"; \
	  for api in $(MISSING_API_DIRS); do $(ECHO) "      " $$api; done);
	@false
endif

ifeq ($(CCACHE),1)
BUILD_CONF_CCACHE := yes
else
BUILD_CONF_CCACHE :=
endif

$(BUILD_CONF): checked_src_archive checked_api_archives
	$(VERBOSE)mkdir -p $(dir $@)
	$(VERBOSE) \
	( echo "GENODE_DIR := $(GENODE_DIR)"; \
	  echo "BASE_DIR := $(GENODE_DIR)/repos/base"; \
	  echo "override CCACHE := $(BUILD_CONF_CCACHE)"; \
	  echo "override KERNEL :="; \
	  echo "REPOSITORIES := $(SRC_DIR)"; \
	  for api in $(USED_APIS); do \
	      echo "REPOSITORIES += $(DEPOT_API_DIR)/$$api"; done \
	) > $(BUILD_CONF)

$(SPECS_CONF): checked_spec_argument
	$(VERBOSE)mkdir -p $(dir $@)
	$(VERBOSE)echo "SPECS += genode $(SPEC)" > $@

$(TOOLS_CONF):
	$(VERBOSE)mkdir -p $(dir $@)
	$(VERBOSE)cp $(GENODE_DIR)/repos/base/etc/tools.conf $@

$(BUILD_MK):
	$(VERBOSE)mkdir -p $(dir $@)
	$(VERBOSE)ln -sf $(GENODE_DIR)/tool/builddir/build.mk $@

$(BUILD_CONF) $(SPECS_CONF) $(TOOLS_CONF) $(BUILD_MK): wiped_build_dir

wiped_build_dir:
	$(VERBOSE)rm -rf $(DEPOT_ARCHIVE_BUILD_DIR)


#
# Invoke the Genode build system in the build directory
#

ifeq ($(KEEP_BUILD_DIR),)
RM_BUILD_DIR_CMD := rm -rf $(DEPOT_ARCHIVE_BUILD_DIR)
else
RM_BUILD_DIR_CMD := true
endif

$(DEPOT_ARCHIVE_BUILD_DIR)/bin: $(BUILD_CONF) $(SPECS_CONF) $(TOOLS_CONF) $(BUILD_MK)
	$(VERBOSE)$(MAKE) -C $(DEPOT_ARCHIVE_BUILD_DIR) $(BUILD_ARG) ||\
	          ( $(RM_BUILD_DIR_CMD); false )

$(DEPOT_ARCHIVE_BUILD_DIR)/debug: $(DEPOT_ARCHIVE_BUILD_DIR)/bin

#
# Extract build results from build directory into binary-archive and
# debug-archive directories
#

$(DEPOT_BIN_ARCHIVE_DIR): $(DEPOT_ARCHIVE_BUILD_DIR)/bin
	$(VERBOSE)mkdir -p $@
	$(VERBOSE)find $< -maxdepth 0 -not -empty -exec cp -rL $</* $@/ \;
	@$(ECHO) "$(DARK_COL)created$(DEFAULT_COL) $(USER)/bin/$(SPEC)/$(VERSIONED_ARCHIVE)"

$(DEPOT_DBG_ARCHIVE_DIR): $(DEPOT_ARCHIVE_BUILD_DIR)/debug
	$(VERBOSE)mkdir -p $@
	$(VERBOSE)find $< -name *.debug -exec cp {} $@/ \;
	@$(ECHO) "$(DARK_COL)created$(DEFAULT_COL) $(USER)/dbg/$(SPEC)/$(VERSIONED_ARCHIVE)"

INGREDIENTS        := $(addprefix src/,$(ARCHIVE)) $(addprefix api/,$(USED_APIS))
INGREDIENTS_HASHES := $(foreach I,$(INGREDIENTS),$(call file_content,$(DEPOT_DIR)/$(USER)/$I.hash))

$(DEPOT_BIN_HASH_FILE): $(DEPOT_BIN_ARCHIVE_DIR)
	$(VERBOSE)echo "$(INGREDIENTS_HASHES)" > $@

$(TARGET): $(DEPOT_BIN_HASH_FILE)

ifneq ($(DBG),)
$(TARGET): $(DEPOT_DBG_ARCHIVE_DIR)
endif

#
# Remove intermediate build artifacts
#

ifeq ($(KEEP_BUILD_DIR),)
$(TARGET): remove_build_dir_when_done
remove_build_dir_when_done: $(DEPOT_BIN_ARCHIVE_DIR)
	$(VERBOSE)$(RM_BUILD_DIR_CMD)
ifneq ($(DBG),)
remove_build_dir_when_done: $(DEPOT_DBG_ARCHIVE_DIR)
endif
endif


